﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Hvam.SharePoint.Search.Language;
using Hvam.SharePoint.Search.Query.Site;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Taxonomy;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Hvam.SharePoint.Search.Tests.Internal.Query.Site
{
    [TestClass]
    public class CamlExpressionVisitorTests
    {
        [TestMethod]
        public void CamlExpressionVisitorEqual()
        {
            // Arrange
            using (var site = new SPSite(TestConstants.Site))
            {
                using (var web = site.OpenWeb())
                {
                    var expression =
                        Expression.Equal(
                            KeywordQueryExpression.Property("id"),
                            Expression.Constant("1"));
                    var visitor = new CamlExpressionVisitor();
                    var availableFields = new Dictionary<string, SPField>(StringComparer.OrdinalIgnoreCase);
                    foreach (var field in web.AvailableFields.OfType<SPField>())
                    {
                        availableFields.Add(field.InternalName, field);
                    }

                    // Act
                    var caml = visitor.Translate(site, availableFields, expression);

                    // Assert
                    Assert.AreEqual(string.Format("<Eq><FieldRef ID=\"{0}\"/><Value Type=\"Counter\">1</Value></Eq>", SPBuiltInFieldId.ID), caml);
                }
            }
        }

        [TestMethod]
        public void CamlExpressionVisitorEqualExternalDataSource()
        {
            // Arrange
            using (var site = new SPSite(TestConstants.Site))
            {
                using (var web = site.OpenWeb())
                {
                    var expression =
                        Expression.Equal(
                            KeywordQueryExpression.Property("id"),
                            Expression.Constant("1"));
                    var visitor =
                        new CamlExpressionVisitor
                            {
                                ExternalDataSource = true
                            };
                    var availableFields = new Dictionary<string, SPField>(StringComparer.OrdinalIgnoreCase);
                    foreach (var field in web.AvailableFields.OfType<SPField>())
                    {
                        availableFields.Add(field.InternalName, field);
                    }

                    // Act
                    var caml = visitor.Translate(site, availableFields, expression);

                    // Assert
                    Assert.AreEqual("<Eq><FieldRef Name=\"ID\"/><Value Type=\"Counter\">1</Value></Eq>", caml);
                }
            }
        }

        [TestMethod]
        public void CamlExpressionVisitorGreaterThan()
        {
            // Arrange
            using (var site = new SPSite(TestConstants.Site))
            {
                using (var web = site.OpenWeb())
                {
                    var expression =
                        Expression.GreaterThan(
                            KeywordQueryExpression.Property("id"),
                            Expression.Constant("1"));
                    var visitor = new CamlExpressionVisitor();
                    var availableFields = 
                        new Dictionary<string, SPField>(StringComparer.OrdinalIgnoreCase)
                            {
                                {"ID", web.AvailableFields[SPBuiltInFieldId.ID]}
                            };

                    // Act
                    var caml = visitor.Translate(site, availableFields, expression);

                    // Assert
                    Assert.AreEqual(string.Format("<Gt><FieldRef ID=\"{0}\"/><Value Type=\"Counter\">1</Value></Gt>", SPBuiltInFieldId.ID), caml);
                }
            }
        }

        [TestMethod]
        public void CamlExpressionVisitorNotEqual()
        {
            // Arrange
            using (var site = new SPSite(TestConstants.Site))
            {
                using (var web = site.OpenWeb())
                {
                    var expression =
                        Expression.NotEqual(
                            KeywordQueryExpression.Property("id"),
                            Expression.Constant("1"));
                    var visitor = new CamlExpressionVisitor();
                    var availableFields =
                        new Dictionary<string, SPField>(StringComparer.OrdinalIgnoreCase)
                            {
                                {"ID", web.AvailableFields[SPBuiltInFieldId.ID]}
                            };

                    // Act
                    var caml = visitor.Translate(site, availableFields, expression);

                    // Assert
                    Assert.AreEqual(string.Format("<Neq><FieldRef ID=\"{0}\"/><Value Type=\"Counter\">1</Value></Neq>", SPBuiltInFieldId.ID), caml);
                }
            }
        }

        [TestMethod]
        public void CamlExpressionVisitorEqualEmpty()
        {
            // Arrange
            using (var site = new SPSite(TestConstants.Site))
            {
                using (var web = site.OpenWeb())
                {
                    var expression =
                        Expression.Equal(
                            KeywordQueryExpression.Property("title"),
                            Expression.Constant(""));
                    var visitor = new CamlExpressionVisitor();
                    var availableFields =
                        new Dictionary<string, SPField>(StringComparer.OrdinalIgnoreCase)
                            {
                                {"Title", web.AvailableFields[SPBuiltInFieldId.Title]}
                            };

                    // Act
                    var caml = visitor.Translate(site, availableFields, expression);

                    // Assert
                    Assert.AreEqual(
                        string.Format(
                            "<Or><IsNull><FieldRef ID=\"{0}\"/></IsNull><Eq><FieldRef ID=\"{0}\"/><Value Type=\"Text\"></Value></Eq></Or>",
                            SPBuiltInFieldId.Title), caml);
                }
            }
        }

        [TestMethod]
        public void CamlExpressionVisitorNotEqualEmpty()
        {
            // Arrange
            using (var site = new SPSite(TestConstants.Site))
            {
                using (var web = site.OpenWeb())
                {
                    var expression =
                        Expression.NotEqual(
                            KeywordQueryExpression.Property("title"),
                            Expression.Constant(""));
                    var visitor = new CamlExpressionVisitor();
                    var availableFields =
                        new Dictionary<string, SPField>(StringComparer.OrdinalIgnoreCase)
                            {
                                {"Title", web.AvailableFields[SPBuiltInFieldId.Title]}
                            };

                    // Act
                    var caml = visitor.Translate(site, availableFields, expression);

                    // Assert
                    Assert.AreEqual(
                        string.Format(
                            "<And><IsNotNull><FieldRef ID=\"{0}\"/></IsNotNull><Neq><FieldRef ID=\"{0}\"/><Value Type=\"Text\"></Value></Neq></And>",
                            SPBuiltInFieldId.Title), caml);
                }
            }
        }

        [TestMethod]
        public void CamlExpressionVisitorTranslateContains()
        {
            // Arrange
            using (var site = new SPSite(TestConstants.Site)) 
            {
                using (var web = site.OpenWeb())
                {
                    var expression =
                        KeywordQueryExpression.Contains(
                            KeywordQueryExpression.Property("title"),
                            Expression.Constant("title"));
                    var visitor = new CamlExpressionVisitor();
                    var availableFields = new Dictionary<string, SPField>(StringComparer.OrdinalIgnoreCase);
                    foreach (var field in web.AvailableFields.OfType<SPField>())
                    {
                        availableFields.Add(field.InternalName, field);
                    }

                    // Act
                    var caml = visitor.Translate(site, availableFields, expression);

                    // Assert
                    Assert.AreEqual(string.Format("<Contains><FieldRef ID=\"{0}\"/><Value Type=\"Text\">title</Value></Contains>", SPBuiltInFieldId.Title), caml);
                }
            }
        }

        [TestMethod]
        [ExpectedException(typeof(CamlTranslationException))]
        public void CamlExpressionVisitorTranslateException()
        {
            // Arrange
            using (var site = new SPSite(TestConstants.Site))
            {
                using (var web = site.OpenWeb())
                {
                    var name = Guid.NewGuid().ToString().Replace("-", string.Empty);

                    var expression =
                        Expression.Equal(
                            KeywordQueryExpression.Property(name),
                            Expression.Constant("1"));
                    var visitor = new CamlExpressionVisitor();
                    var availableFields = new Dictionary<string, SPField>(StringComparer.OrdinalIgnoreCase);
                    foreach (var field in web.AvailableFields.OfType<SPField>())
                    {
                        availableFields.Add(field.InternalName, field);
                    }

                    // Act
                    visitor.Translate(site, availableFields, expression);
                }
            }
        }

        [TestMethod]
        public void CamlExpressionVisitorTranslateListField()
        {
            // Arrange
            using (var site = new SPSite(TestConstants.Site))
            {
                using (var web = site.OpenWeb())
                {
                    var name = Guid.NewGuid().ToString().Replace("-", string.Empty);

                    var listId = web.Lists.Add(name, name, SPListTemplateType.GenericList);
                    var list = web.Lists[listId];

                    // Add custom field to list
                    list.Fields.Add(name, SPFieldType.Number, false);
                    var field = list.Fields.TryGetFieldByStaticName(name);

                    // Query custom field
                    var expression =
                        Expression.Equal(
                            KeywordQueryExpression.Property(name),
                            Expression.Constant("1"));
                    var visitor = new CamlExpressionVisitor();
                    var availableFields = new Dictionary<string, SPField>(StringComparer.OrdinalIgnoreCase);

                    foreach (var f in list.Fields.OfType<SPField>())
                    {
                        availableFields.Add(f.InternalName, f);
                    }

                    // Act
                    var caml = visitor.Translate(site, availableFields, expression);

                    // Assert
                    Assert.AreEqual(string.Format("<Eq><FieldRef ID=\"{0}\"/><Value Type=\"Number\">1</Value></Eq>", field.Id), caml);

                    list.Delete();
                }
            }
        }

        [TestMethod]
        public void CamlExpressionVisitorTranslateTaxonomy()
        {
            // Arrange
            using (var site = new SPSite(TestConstants.Site))
            {
                using (var web = site.OpenWeb())
                {
                    var session = new TaxonomySession(site);
                    var termStore = session.DefaultKeywordsTermStore;
                    var termSet = termStore.GetTermSet(TestConstants.TermSetId);
                    if (termSet == null)
                    {
                        Assert.Fail("TermSet not found.");
                    }
                    var term = termSet.GetTerm(new Guid("1f65bfc5-465d-4cf4-ac37-39d224d04234"));

                    const string name = "SiteSearchTaxonomyField";
                    var f = web.Fields.TryGetFieldByStaticName(name);
                    if (f != null)
                    {
                        f.Delete();
                    }

                    var taxonomyField = (TaxonomyField) web.Fields.CreateNewField("TaxonomyFieldType", "SiteSearchTaxonomyField");
                    taxonomyField.SspId = termSet.TermStore.Id;
                    taxonomyField.TermSetId = termSet.Id;
                    
                    web.Fields.Add(taxonomyField);

                    var title = Guid.NewGuid().ToString().Replace("-", string.Empty);
                    var id = web.Lists.Add(title, string.Empty, SPListTemplateType.GenericList);
                    var list = web.Lists[id];

                    list.Fields.Add(web.AvailableFields.TryGetFieldByStaticName("SiteSearchTaxonomyField"));

                    list.Update();

                    taxonomyField = (TaxonomyField) list.Fields.TryGetFieldByStaticName("SiteSearchTaxonomyField");

                    var item = list.Items.Add();
                    item[SPBuiltInFieldId.Title] = "Title";
                    taxonomyField.SetFieldValue(item, term);
                    item.Update();

                    var expression =
                        KeywordQueryExpression.Contains(
                            KeywordQueryExpression.Property("owstaxIdSiteSearchTaxonomyField"),
                            Expression.Constant("#01f65bfc5-465d-4cf4-ac37-39d224d04234"));

                    var availableFields = new Dictionary<string, SPField>(StringComparer.OrdinalIgnoreCase);
                    foreach (var field in web.AvailableFields.OfType<SPField>())
                    {
                        availableFields.Add(field.InternalName, field);
                    }

                    var visitor = new CamlExpressionVisitor();

                    // Act
                    var caml = visitor.Translate(site, availableFields, expression);

                    // Assert
                    list.Delete();

                    var ids = TaxonomyField.GetWssIdsOfTerm(site, termStore.Id, termSet.Id, term.Id, false, 1);

                    Assert.AreEqual(
                        string.Format("<In><FieldRef LookupId=\"TRUE\" ID=\"{0}\"/><Values><Value Type=\"Integer\">{1}</Value></Values></In>", taxonomyField.Id, ids[0]),
                        caml);
                }
            }
        }

        [TestMethod]
        public void CamlExpressionVisitorTranslateDateTime()
        {
            // Arrange
            using (var site = new SPSite(TestConstants.Site))
            {
                using (var web = site.OpenWeb())
                {
                    var expression =
                        Expression.GreaterThanOrEqual(
                            KeywordQueryExpression.Property("modified"),
                            Expression.Constant("2012-05-05"));
                    var visitor = new CamlExpressionVisitor();
                    var availableFields = new Dictionary<string, SPField>(StringComparer.OrdinalIgnoreCase);
                    foreach (var field in web.AvailableFields.OfType<SPField>())
                    {
                        availableFields.Add(field.InternalName, field);
                    }

                    // Act
                    var caml = visitor.Translate(site, availableFields, expression);

                    // Assert
                    Assert.AreEqual(string.Format("<Geq><FieldRef ID=\"{0}\"/><Value Type=\"DateTime\">2012-05-05</Value></Geq>", SPBuiltInFieldId.Modified), caml);
                }
            }
        }

        [TestMethod]
        public void CamlExpressionVisitorTranslateDateTimeIncludeTimeValue()
        {
            // Arrange
            using (var site = new SPSite(TestConstants.Site))
            {
                using (var web = site.OpenWeb())
                {
                    var expression =
                        Expression.GreaterThanOrEqual(
                            KeywordQueryExpression.Property("modified"),
                            Expression.Constant("2012-05-05T00:00:00Z"));
                    var visitor = new CamlExpressionVisitor();
                    var availableFields = new Dictionary<string, SPField>(StringComparer.OrdinalIgnoreCase);
                    foreach (var field in web.AvailableFields.OfType<SPField>())
                    {
                        availableFields.Add(field.InternalName, field);
                    }

                    // Act
                    var caml = visitor.Translate(site, availableFields, expression);

                    // Assert
                    Assert.AreEqual(string.Format("<Geq><FieldRef ID=\"{0}\"/><Value Type=\"DateTime\" IncludeTimeValue=\"TRUE\">2012-05-05T00:00:00Z</Value></Geq>", SPBuiltInFieldId.Modified), caml);
                }
            }
        }
    }
}
